package com.atguigu.daijia.driver.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.customer.client.CustomerInfoFeignClient;
import com.atguigu.daijia.dispatch.client.NewOrderFeignClient;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.driver.service.OrderService;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.map.client.MapFeignClient;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.order.OrderInfo;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.map.CalculateDrivingLineForm;
import com.atguigu.daijia.model.form.order.OrderFeeForm;
import com.atguigu.daijia.model.form.order.StartDriveForm;
import com.atguigu.daijia.model.form.order.UpdateOrderBillForm;
import com.atguigu.daijia.model.form.order.UpdateOrderCartForm;
import com.atguigu.daijia.model.form.rules.FeeRuleRequestForm;
import com.atguigu.daijia.model.form.rules.ProfitsharingRuleRequestForm;
import com.atguigu.daijia.model.form.rules.RewardRuleRequestForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.customer.CustomerLoginVo;
import com.atguigu.daijia.model.vo.map.DrivingLineVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.model.vo.order.*;
import com.atguigu.daijia.model.vo.rules.FeeRuleResponseVo;
import com.atguigu.daijia.model.vo.rules.ProfitsharingRuleResponseVo;
import com.atguigu.daijia.model.vo.rules.RewardRuleResponseVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.atguigu.daijia.rules.client.FeeRuleFeignClient;
import com.atguigu.daijia.rules.client.ProfitsharingRuleFeignClient;
import com.atguigu.daijia.rules.client.RewardRuleFeignClient;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderInfoFeignClient orderInfoFeignClient;
    private final NewOrderFeignClient newOrderFeignClient;
    private final MapFeignClient mapFeignClient;
    private final LocationFeignClient locationFeignClient;
    private final FeeRuleFeignClient feeRuleFeignClient;
    private final DriverInfoFeignClient driverInfoFeignClient;
    private final RewardRuleFeignClient rewardRuleFeignClient;
    private final ProfitsharingRuleFeignClient profitsharingRuleFeignClient;
    private final ThreadPoolExecutor threadPoolExecutor;
    private final CustomerInfoFeignClient customerInfoFeignClient;

    /**
     * 查询订单状态
     * @param orderId
     * @return
     */
    @Override
    public Integer getOrderStatus(Long orderId) {
        return orderInfoFeignClient.getOrderStatus(orderId).getData();
    }

    /**
     * 查询司机临时队列中的最新订单数据
     * @param driverId
     * @return
     */
    @Override
    public List<NewOrderDataVo> findNewOrderQueueData(Long driverId) {
        return newOrderFeignClient.findNewOrderQueueData(driverId).getData();
    }

    /**
     * 司机抢单
     * @param driverId
     * @param orderId
     * @return
     */
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        return orderInfoFeignClient.robNewOrder(driverId, orderId).getData();
    }

    /**
     * 查询司机执行中订单基本信息
     * @param driverId
     * @return
     */
    @Override
    public CurrentOrderInfoVo searchDriverCurrentOrder(Long driverId) {
        return orderInfoFeignClient.searchDriverCurrentOrder(driverId).getData();
    }

    /**
     * 根据订单 ID 查询当前司机的订单信息
     * @param driverId
     * @param orderId
     * @return
     */
    @Override
    public OrderInfoVo getOrderInfo(Long driverId, Long orderId) {
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        if (driverId != orderInfo.getDriverId().longValue()) {
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }

        // 订单状态为结束服务，获取账单信息、分账信息
        OrderBillVo orderBillVo = new OrderBillVo();
        OrderProfitsharingVo orderProfitsharingVo = new OrderProfitsharingVo();
        if (orderInfo.getStatus() >= OrderStatus.END_SERVICE.getStatus()) {
            // 订单已经结束，可以获取到订单的账单信息、分账信息，用于订单结算
            orderBillVo = orderInfoFeignClient.getOrderBillInfo(orderId).getData();
            orderProfitsharingVo = orderInfoFeignClient.getOrderProfitsharing(orderId).getData();
        }

        // 封装返回对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        orderInfoVo.setOrderId(orderInfo.getId());
        BeanUtils.copyProperties(orderInfo, orderInfoVo);
        orderInfoVo.setOrderBillVo(orderBillVo);
        orderInfoVo.setOrderProfitsharingVo(orderProfitsharingVo);
        return orderInfoVo;
    }

    /**
     * 计算接单司机到订单起始点最佳路线
     * @param calculateDrivingLineForm
     * @return
     */
    @Override
    public DrivingLineVo calculateRouteToOrderStartPoint(CalculateDrivingLineForm calculateDrivingLineForm) {
        return mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData();
    }

    /**
     * 接单司机到达代驾订单起始点
     * @param orderId
     * @param driverId
     * @return
     */
    @Override
    public Boolean driverArriveStartLocation(Long orderId, Long driverId) {
        // 防止刷单：计算司机到订单起点的距离，如果距离大于 1km，则不允许点击已到达订单起始点
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        OrderLocationVo orderLocationVo = locationFeignClient.getDriverLocationFromCache(orderId).getData();
        // 计算订单起点和司机的距离
        /*double distance = LocationUtil.getDistance(orderInfo.getStartPointLatitude().doubleValue(), orderInfo.getStartPointLongitude().doubleValue(), orderLocationVo.getLatitude().doubleValue(), orderLocationVo.getLongitude().doubleValue());
        if (distance > SystemConstant.DRIVER_START_LOCATION_DISTION) {
            throw new GuiguException(ResultCodeEnum.DRIVER_START_LOCATION_DISTION_ERROR);
        }*/
        return orderInfoFeignClient.driverArriveStartLocation(orderId, driverId).getData();
    }

    /**
     * 更新代驾车辆信息
     * @param updateOrderCartForm
     * @return
     */
    @Override
    public Boolean updateOrderCar(UpdateOrderCartForm updateOrderCartForm) {
        return orderInfoFeignClient.updateOrderCar(updateOrderCartForm).getData();
    }

    /**
     * 开始代驾服务
     * @param startDriveForm
     * @return
     */
    @Override
    public Boolean startDrive(StartDriveForm startDriveForm) {
        return orderInfoFeignClient.startDrive(startDriveForm).getData();
    }

    /**
     * 结束代驾服务：串行执行
     * @param orderFeeForm
     * @return
     */
    // @Override
    public Boolean endDrive01(OrderFeeForm orderFeeForm) {
        // 1 判断是否是当前司机的订单
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData();
        if (orderFeeForm.getDriverId() != orderInfo.getDriverId().longValue()) {
            throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
        }
        // 2 防止刷单，计算当前司机距离订单终点的距离，如果大于 2km，则不允许结束代驾服务
        /*OrderServiceLastLocationVo orderServiceLastLocationVo = locationFeignClient.getOrderServiceLastLocation(orderFeeForm.getOrderId()).getData();
        // 计算订单终点和司机的距离
        double distance = LocationUtil.getDistance(orderInfo.getEndPointLatitude().doubleValue(), orderInfo.getEndPointLongitude().doubleValue(), orderServiceLastLocationVo.getLatitude().doubleValue(), orderServiceLastLocationVo.getLongitude().doubleValue());
        if (distance > SystemConstant.DRIVER_END_LOCATION_DISTION) {
            throw new GuiguException(ResultCodeEnum.DRIVER_END_LOCATION_DISTION_ERROR);
        }*/

        // 3 计算当前订单的实际代驾里程
        BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData();
        log.info("结束代驾，订单实际里程：{}", realDistance);

        // 4 计算当前订单的代驾费用
        FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
        feeRuleRequestForm.setDistance(realDistance);
        feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime());
        // 代驾司机等待时间（分钟） = 开始代驾服务时间 - 到达代驾订单起始点的时间
        int waitMinute = (int) Math.abs(orderInfo.getStartServiceTime().getTime() - orderInfo.getArriveTime().getTime()) / (60 * 1000);
        feeRuleRequestForm.setWaitMinute(waitMinute);
        log.info("结束代驾，代驾费用参数：{}", JSON.toJSONString(feeRuleRequestForm));
        FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData();
        log.info("结束代驾，代驾费用详情：{}", JSON.toJSONString(feeRuleResponseVo));

        // 5 计算系统奖励
        // 司机当天开始接单时间
        DriverSet driverSet = driverInfoFeignClient.getDriverSet(orderInfo.getDriverId()).getData();
        String startTime = new DateTime(driverSet.getUpdateTime()).toString("yyyy-MM-dd HH:mm:ss");
        // 当前订单代驾服务结束时间
        String endTime = new DateTime(orderInfo.getEndServiceTime()).toString("yyyy-MM-dd HH:mm:ss");
        // 获取该时段司机的订单数
        Long orderNum = orderInfoFeignClient.getOrderNumByTime(startTime, endTime, orderInfo.getDriverId()).getData();
        // 封装计算奖励所需参数
        RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm();
        rewardRuleRequestForm.setStartTime(driverSet.getUpdateTime());
        rewardRuleRequestForm.setOrderNum(orderNum);
        log.info("结束代驾，系统奖励参数：{}", JSON.toJSONString(rewardRuleRequestForm));
        RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData();
        log.info("结束代驾，系统奖励详情：{}", JSON.toJSONString(rewardRuleResponseVo));

        // 6 计算分账信息
        ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm();
        // 订单金额 = 代驾费用 + 其他（停车费、路桥费、乘客好处费、其他费用）
        BigDecimal orderAmount = feeRuleResponseVo.getTotalAmount()
                .add(orderFeeForm.getTollFee())
                .add(orderFeeForm.getParkingFee())
                .add(orderFeeForm.getOtherFee())
                .add(orderInfo.getFavourFee());
        profitsharingRuleRequestForm.setOrderAmount(orderAmount);
        profitsharingRuleRequestForm.setOrderNum(orderNum);
        log.info("结束代驾，分账参数：{}", JSON.toJSONString(profitsharingRuleRequestForm));
        ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData();
        log.info("结束代驾，分账信息：{}", JSON.toJSONString(profitsharingRuleResponseVo));

        // 7 更新数据库表：订单信息表、订单状态日志表、订单账单表
        UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm();
        // 基本信息和其他费用
        BeanUtils.copyProperties(orderFeeForm, updateOrderBillForm);
        updateOrderBillForm.setRealDistance(realDistance);
        updateOrderBillForm.setFavourFee(orderInfo.getFavourFee());
        // 订单奖励信息
        BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm);
        // 代驾费用信息
        BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm);
        // 订单分账信息
        BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm);
        log.info("结束代驾，更新数据库表所需参数：{}", JSON.toJSONString(updateOrderBillForm));
        // 更新数据库表：订单信息表、订单状态日志表、订单账单表
        orderInfoFeignClient.endDrive(updateOrderBillForm);
        return true;
    }

    /**
     * 结束代驾服务：异步编排模式
     * @param orderFeeForm
     * @return
     */
    @Override
    @SneakyThrows
    public Boolean endDrive(OrderFeeForm orderFeeForm) {

        // 1 获取订单信息
        CompletableFuture<OrderInfo> orderInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderFeeForm.getOrderId()).getData();
            // 判断是否是当前司机的订单
            if (orderFeeForm.getDriverId() != orderInfo.getDriverId().longValue()) {
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }
            return orderInfo;
        }, threadPoolExecutor);

        // 2 防止刷单，如果当前司机位置距离订单终点大于 2km，则不允许结束代驾服务
        // 获取代驾服务期间司机的最新位置信息
        CompletableFuture<OrderServiceLastLocationVo> orderServiceLastLocationVoCompletableFuture = CompletableFuture.supplyAsync(() -> locationFeignClient.getOrderServiceLastLocation(orderFeeForm.getOrderId()).getData(), threadPoolExecutor);

        // 等待所有任务完成
        CompletableFuture.allOf(orderInfoCompletableFuture, orderServiceLastLocationVoCompletableFuture).join();
        // 获取订单信息
        OrderInfo orderInfo = orderInfoCompletableFuture.get();
        // 获取订单终点位置
        OrderServiceLastLocationVo orderServiceLastLocationVo = orderServiceLastLocationVoCompletableFuture.get();

        // 计算订单终点和司机最新位置的距离
        double distance = LocationUtil.getDistance(orderInfo.getEndPointLatitude().doubleValue(), orderInfo.getEndPointLongitude().doubleValue(), orderServiceLastLocationVo.getLatitude().doubleValue(), orderServiceLastLocationVo.getLongitude().doubleValue());
        // 防止刷单，当前司机位置距离订单终点的距离如果大于 2km，则不允许结束代驾服务
        // if (distance > SystemConstant.DRIVER_END_LOCATION_DISTION) {
        //     throw new GuiguException(ResultCodeEnum.DRIVER_END_LOCATION_DISTION_ERROR);
        // }

        // 3 计算当前订单的实际代驾里程
        CompletableFuture<BigDecimal> realDistanceCompletableFuture = CompletableFuture.supplyAsync(() -> {
            BigDecimal realDistance = locationFeignClient.calculateOrderRealDistance(orderFeeForm.getOrderId()).getData();
            log.info("结束代驾，订单实际里程：{}", realDistance);
            return realDistance;
        }, threadPoolExecutor);

        // 4 计算当前订单的代驾费用，依赖于 realDistanceCompletableFuture 的结果
        CompletableFuture<FeeRuleResponseVo> feeRuleResponseVoCompletableFuture = realDistanceCompletableFuture.thenApplyAsync(realDistance -> {
            FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
            feeRuleRequestForm.setDistance(realDistance);
            feeRuleRequestForm.setStartTime(orderInfo.getStartServiceTime());
            // 代驾司机等待时间（分钟） = 开始代驾服务时间 - 到达代驾订单起始点的时间
            int waitMinute = (int) Math.abs(orderInfo.getStartServiceTime().getTime() - orderInfo.getArriveTime().getTime()) / (60 * 1000);
            feeRuleRequestForm.setWaitMinute(waitMinute);
            log.info("结束代驾，代驾费用参数：{}", JSON.toJSONString(feeRuleRequestForm));
            FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData();
            log.info("结束代驾，代驾费用详情：{}", JSON.toJSONString(feeRuleResponseVo));
            return feeRuleResponseVo;
        }, threadPoolExecutor);


        // 5 计算系统奖励
        // 获取司机当天设置信息
        CompletableFuture<DriverSet> driverSetCompletableFuture = CompletableFuture.supplyAsync(() -> driverInfoFeignClient.getDriverSet(orderInfo.getDriverId()).getData(), threadPoolExecutor);
        // 计算司机当天的订单数，依赖于 driverSetCompletableFuture 的结果
        CompletableFuture<Long> orderNUmCompletableFuture = driverSetCompletableFuture.thenApplyAsync(driverSet -> {
            // 司机当天开始接单时间
            String startTime = new DateTime(driverSet.getUpdateTime()).toString("yyyy-MM-dd HH:mm:ss");
            // 当前订单代驾服务结束时间
            String endTime = new DateTime(orderInfo.getEndServiceTime()).toString("yyyy-MM-dd HH:mm:ss");
            // 获取该时段司机的订单数
            return orderInfoFeignClient.getOrderNumByTime(startTime, endTime, orderInfo.getDriverId()).getData();
        }, threadPoolExecutor);

        // 计算系统奖励，依赖于 driverSetCompletableFuture、orderNUmCompletableFuture 的结果
        CompletableFuture<RewardRuleResponseVo> rewardRuleResponseVoCompletableFuture = driverSetCompletableFuture.thenCombineAsync(orderNUmCompletableFuture, (driverSet, orderNum) -> {
            RewardRuleRequestForm rewardRuleRequestForm = new RewardRuleRequestForm();
            rewardRuleRequestForm.setStartTime(driverSet.getUpdateTime());
            rewardRuleRequestForm.setOrderNum(orderNum);
            log.info("结束代驾，系统奖励参数：{}", JSON.toJSONString(rewardRuleRequestForm));
            RewardRuleResponseVo rewardRuleResponseVo = rewardRuleFeignClient.calculateOrderRewardFee(rewardRuleRequestForm).getData();
            log.info("结束代驾，系统奖励详情：{}", JSON.toJSONString(rewardRuleResponseVo));
            return rewardRuleResponseVo;
        }, threadPoolExecutor);

        // 6 计算分账信息，依赖于 feeRuleResponseVoCompletableFuture、orderNUmCompletableFuture 的结果
        CompletableFuture<ProfitsharingRuleResponseVo> profitsharingRuleResponseVoCompletableFuture = feeRuleResponseVoCompletableFuture.thenCombineAsync(orderNUmCompletableFuture, (feeRuleResponseVo, orderNum) -> {
            ProfitsharingRuleRequestForm profitsharingRuleRequestForm = new ProfitsharingRuleRequestForm();
            // 订单金额 = 代驾费用 + 其他（停车费、路桥费、乘客好处费、其他费用）
            BigDecimal orderAmount = feeRuleResponseVo.getTotalAmount()
                    .add(orderFeeForm.getTollFee())
                    .add(orderFeeForm.getParkingFee())
                    .add(orderFeeForm.getOtherFee())
                    .add(orderInfo.getFavourFee());
            profitsharingRuleRequestForm.setOrderAmount(orderAmount);
            profitsharingRuleRequestForm.setOrderNum(orderNum);
            log.info("结束代驾，分账参数：{}", JSON.toJSONString(profitsharingRuleRequestForm));
            ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleFeignClient.calculateOrderProfitsharingFee(profitsharingRuleRequestForm).getData();
            log.info("结束代驾，分账信息：{}", JSON.toJSONString(profitsharingRuleResponseVo));
            return profitsharingRuleResponseVo;
        }, threadPoolExecutor);

        // 等待所有任务完成
        CompletableFuture.allOf(realDistanceCompletableFuture, feeRuleResponseVoCompletableFuture, driverSetCompletableFuture, orderNUmCompletableFuture, rewardRuleResponseVoCompletableFuture, profitsharingRuleResponseVoCompletableFuture).join();

        // 获取执行结果
        BigDecimal realDistance = realDistanceCompletableFuture.get();
        FeeRuleResponseVo feeRuleResponseVo = feeRuleResponseVoCompletableFuture.get();
        RewardRuleResponseVo rewardRuleResponseVo = rewardRuleResponseVoCompletableFuture.get();
        ProfitsharingRuleResponseVo profitsharingRuleResponseVo = profitsharingRuleResponseVoCompletableFuture.get();

        // 7 更新数据库表：订单信息表、订单状态日志表、订单账单表
        UpdateOrderBillForm updateOrderBillForm = new UpdateOrderBillForm();
        // 基本信息和其他费用
        BeanUtils.copyProperties(orderFeeForm, updateOrderBillForm);
        updateOrderBillForm.setRealDistance(realDistance);
        updateOrderBillForm.setFavourFee(orderInfo.getFavourFee());
        // 订单奖励信息
        BeanUtils.copyProperties(rewardRuleResponseVo, updateOrderBillForm);
        // 代驾费用信息
        BeanUtils.copyProperties(feeRuleResponseVo, updateOrderBillForm);
        // 订单分账信息
        BeanUtils.copyProperties(profitsharingRuleResponseVo, updateOrderBillForm);
        log.info("结束代驾，更新数据库表所需参数：{}", JSON.toJSONString(updateOrderBillForm));
        // 更新数据库表：订单信息表、订单状态日志表、订单账单表
        orderInfoFeignClient.endDrive(updateOrderBillForm);
        return true;
    }

    /**
     * 分页查询司机代驾订单列表
     * @param driverId
     * @param page
     * @param limit
     * @return
     */
    @Override
    public PageVo findDriverOrderPage(Long driverId, Long page, Long limit) {
        return orderInfoFeignClient.findDriverOrderPage(driverId, page, limit).getData();
    }

    /**
     * 司机发送账单信息给乘客
     * @param orderId
     * @param driverId
     * @return
     */
    @Override
    public Boolean sendOrderBillInfo(Long orderId, Long driverId) {
        return orderInfoFeignClient.sendOrderBillInfo(orderId, driverId).getData();
    }
}
